/*****************************************************************************/
/* File: Ball.cpp	                                                         */
/* Author: rszulgo                                                           */
/*                                                                           */
/* Description:																 */
/*	Representation of ball object											 */
/*****************************************************************************/
#include "Ball.h"
#include "Bass.h"

CModel* CBall::m_pModel = NULL;
bool CBall::m_bEnableClone = false;

CBall::CBall()
{
	//non-ODE init routines
	m_bMarked = false;	//not marked for remove
	m_fSpeed = 9.5f;
	m_fRadius = 0.44f;
	m_bTrapped = false;

	//m_fTimeToClone = 0.0f;

	//material:
	m_matMaterial.d3Diffuse = double3(0.8, 0.8, 0.8);
	m_matMaterial.d3Specular = double3(0.5, 0.5, 1.0);
	m_matMaterial.dAlpha = 0.0;
	m_matMaterial.dShiness = 50.0;

	//ODE init routines:

	m_body = dBodyCreate( m_pPhysics->GetWorld() );
	
	m_geom = dCreateSphere(m_pPhysics->GetSpace(), m_fRadius);
		
	dGeomSetBody(m_geom, m_body);

	dMassSetSphere(&m_mass, 1, m_fRadius);
	dBodySetMass(m_body, &m_mass);

	dBodySetPosition(m_body, 0.0f, m_fRadius, 8.0f);

	dBodySetData(m_body, this);

	//set initial velocity

	double fVel[3] = {1.5f, 0.0f, 1.0f};	//teporary vector with direction of velocity before normalization
	CUtils::Vec3Norm(fVel);	//normalize direction vector
	CUtils::Vec3Mult(fVel, m_fSpeed);

	dBodySetLinearVel(m_body, fVel[0], fVel[1], fVel[2]);
	
	// set ball surface
	GLfloat fWhiteSurface[4] = {1.0f, 1.0f, 1.0f, 1.0f};

	for (int i=0; i<4; i++)
	{
		m_fBallSurface[i] = fWhiteSurface[i];
	}
}

CBall::~CBall(void)
{
	dBodyDestroy(m_body);
	dGeomDestroy(m_geom);
}

int CBall::Draw()
{
	glPushMatrix();

	SetupMatrices();

	m_pRenderer->SetMaterial(m_matMaterial);
	m_pModel->DrawModel();

	glPopMatrix();
	return 0;
}

int CBall::Update()
{
	//check if ball is trapped by bat
	if(m_bTrapped)
	{
		//if so, move with bat
		const double* dBatPos = m_pBatRecent->GetPosition();

		//for 1-st player ball should have higher z-value, for 2-nd
		//should have lower z-value. Because of this we need sign of z-value of Bat
		float fSign = (float)dBatPos[2]/abs((float)dBatPos[2]);

		dBodySetPosition(m_body, dBatPos[0] + m_fXTrapOffset, m_fRadius, dBatPos[2] - (fSign*(0.51 + m_fRadius)));

		//check if player released the ball
		if(m_pBatRecent->GetRelease() == true)
		{
			//if so, release ball and set dir vector to up-right (like in Arkanoid :P )
			//up-right in player coordinates - so once again we'll need fSign

			m_bTrapped = false;	//release ball
			//dBodySetLinearVel(m_body, 1.0*fSign, 0.0, -1.0*fSign);	//set velocity
		}

		//get bat surface to mark the ball
		m_matMaterial.d3Diffuse = m_pBatRecent->GetColor();
	}
	else
	{
		//if ball is not traped, move normally

		const double* dLinVel = dBodyGetLinearVel(m_body);
		double dVelX = dLinVel[0];
		double dVelZ = dLinVel[2];

		int iSignZ = (int)(dVelZ/abs(dVelZ));
		//control direction of bouncing - cannot bounce in too small angle
		if( abs(dVelZ) < (abs(dVelX)/2) )
		{
			dVelZ = iSignZ * (abs(dVelX)/2);
		}
		//y should be 0, because ball should always move in a horizontal direction
		double dResultVel[3] = {dVelX, 0.0, dVelZ};

		CUtils::Vec3Norm(dResultVel);
		CUtils::Vec3Mult(dResultVel, m_fSpeed);

		dBodySetLinearVel(m_body, dResultVel[0], dResultVel[1], dResultVel[2]);

		// check where the ball is
		const double* dPos = GetPosition();
		if (dPos[0] <= -1 * m_pLevel->GetWidth()/2+0.45 || dPos[0] >= m_pLevel->GetWidth()/2-0.45 || (!g_bTwoPlayers && dPos[2] <= -1 * m_pLevel->GetLength()/2+0.45))
		{
			m_pSoundManager->PlaySoundA("data\\sounds\\wall.mp3",false);
		}

		if(m_bEnableClone)
		{
			CBall* pNewBall = new CBall();
			m_pDrawList->AddObject(pNewBall);
			//set position and velocity
			const double* dPos = dBodyGetPosition(m_body);
			dBodySetPosition(pNewBall->m_body, dPos[0], dPos[1], dPos[2]);
			const double* dVel = dBodyGetLinearVel(m_body);
			dBodySetLinearVel(pNewBall->m_body, -dVel[0], -dVel[1], -dVel[2]);
			//set speed
			pNewBall->SetSpeed(m_fSpeed);
			//set recent bat
			pNewBall->SetRecent(m_pBatRecent);
			EnableClone(false);
		}
	}
	
	m_pBatRecent->SetRelease(false);	//always reset release
	return 0;
}


int CBall::BatHit(CBat* pBat)
{

	//if bat is sticky, ball should be trapped
	if(pBat->IsSticky())
	{	
		//compute offset from bat center
		
		const double* dBatPos = pBat->GetPosition();
		const double* dPos = GetPosition();

		float fXOffset = (float)(dPos[0] - dBatPos[0]);
		Trap(pBat, fXOffset);
		m_pSoundManager->PlaySoundA("data\\sounds\\sticky.wav",false);
	}
	
	//compute ball direction after bounce
	BatBounce(pBat);
	return 0;
}

int CBall::BatBounce(CBat* pBat)
{

	//After hitting a bat ball is bounced out in a direction that is determined by the
	//hitted part of the bat. If hit takes place in the center of the Bat, ball is
	//throw straight forward (in direction of Bat's normal). If hit was in the corner
	//of Bat, ball is throw in the angle of 45 degrees in left or right.

	double fBounceDir[3] = {0.0f, 0.0f, 0.0f};

	//if ball is higher than a bat, probably it's the lower bat
	const double* dBallPos = dBodyGetPosition(m_body);
	const double* dBatPos = pBat->GetPosition();

	double dBatLength = pBat->GetLength();

	if(dBallPos[2] <= dBatPos[2])
	{
		//ok, it's lower bat, so rebound the ball up
		fBounceDir[2] = -1.0;
	}
	else
	{
		fBounceDir[2] = 1.0;
	}

	//now, compute left-right parameter. It depends on the location of the ball
	//against the bat on the x axis.
	fBounceDir[0] = (double)(2*(dBallPos[0] - dBatPos[0]))/dBatLength;

	//normalize velcity direction, and multiply it by speed
	CUtils::Vec3Norm(fBounceDir);
	CUtils::Vec3Mult(fBounceDir, m_fSpeed);

	dBodySetLinearVel(m_body, fBounceDir[0], fBounceDir[1], fBounceDir[2]);

	//set this bat as recently touched ball
	m_pBatRecent = pBat;

	//get bat surface to mark the ball
	m_matMaterial.d3Diffuse = m_pBatRecent->GetColor();
	m_pSoundManager->PlaySoundA("data\\sounds\\bounce.wav",false);

	return 0;
}

int CBall::BonusHit()
{
	m_pSoundManager->PlaySoundA("data\\sounds\\bong.wav",false);
	return 0;
}

int CBall::Trap(CBat* pBat, float fXOffset)
{
	m_pBatRecent = pBat;
	m_bTrapped = true;
	m_fXTrapOffset = fXOffset;
	return 0;
}

int CBall::Out(CBat* pBat)
{	
	m_pSoundManager->PlaySoundA("data\\sounds\\splash.wav",false);

	if(m_pDrawList->GetBallCount() == 1)
	{
		//if it is the last ball trap it
		Trap(pBat, 0.0f);
		pBat->ResetBonuses();
	}
	else
	{
		//if it isn't last ball, delete it, but dont hurt the player
		Remove();
	}
	return 0;
}
